package cn.janescott.inner.space.lib.web.filter;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/*
    使用方式

    1、注册过滤器
    2、设置拦截URL -->> endpoints路径 默认是 /actuator/*
    3、设置初始化参数  -->> ip白名单 使用 , 分割

    @Bean
    public FilterRegistrationBean adminFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(new SpringBootAdminFilter());
        registrationBean.addUrlPatterns("/actuator/*");
        registrationBean.addInitParameter(SpringBootAdminFilter.INCLUDE_ADDRESS, "172.21.118.58");
        return registrationBean;
    }

 */

/**
 * <p>
 * 使用springboot admin时，
 * 由于endpoints全部对web端开放，
 * 在不使用security进行验证的前提下，
 * 为了防止恶意访问，可以指定ip白名单
 * </p>
 * Created by Scott on 2018/7/19
 */
public class SpringBootAdminFilter implements javax.servlet.Filter {
    private Logger logger = LoggerFactory.getLogger(getClass());

    //ip白名单 多个ip使用英文 , 分割
    public static final String INCLUDE_ADDRESS = "INCLUDE_ADDRESS";

    //本地调用
    public static final String LOCALHOST_NAME = "localhost";
    public static final String LOCALHOST_IP = "127.0.0.1";
    //localhost会被解析成一下字段
    private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";

    //regex for ip
    public static final String IP_REGEX = "[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}";

    private List<String> includeAddress = new ArrayList<>();

    /**
     * 获取初始化参数
     * 并验证和赋值
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //默认支持本地调用
        includeAddress.add(LOCALHOST_IP);
        includeAddress.add(LOCALHOST_NAME);
        includeAddress.add(LOCALHOST_IPV6);

        if (null != filterConfig) {
            //获取filter初始化参数
            String initParameter = filterConfig.getInitParameter(INCLUDE_ADDRESS);
            if (!StringUtils.isEmpty(initParameter)) {
                //将多个ip切分
                String[] ips = initParameter.split(",");
                if (!ArrayUtils.isEmpty(ips)) {
                    //使用正则表达式
                    Pattern pattern = Pattern.compile(IP_REGEX);
                    for (String ip : ips) {
                        if (!StringUtils.isEmpty(ip)) {
                            //默认支持本地调用
                            if (StringUtils.equals(LOCALHOST_IPV6, ip)
                                    || StringUtils.equalsIgnoreCase(LOCALHOST_NAME, ip)
                                    || StringUtils.equals(LOCALHOST_IP, ip)) {
                                continue;
                            }
                            //满足则添加
                            if (pattern.matcher(ip).matches()) {
                                includeAddress.add(ip);
                                //不满足报错
                            } else {
                                throw new IllegalArgumentException("illegal ip pattern!");
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //检测 request & response 类型
        if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
            HttpServletRequest servletRequest = (HttpServletRequest) request;
            //hostname
            String hostname = servletRequest.getRemoteHost();
            if (logger.isInfoEnabled()) {
                logger.info("remote host is: " + hostname);
            }
            if (StringUtils.equalsIgnoreCase(hostname, LOCALHOST_NAME)) {
                chain.doFilter(request, response);
                return;
            }
            //ip address
            String ipAddress = servletRequest.getRemoteAddr();
            if (logger.isInfoEnabled()) {
                logger.info("remote address is: " + ipAddress);
            }
            logger.info(ipAddress + ((HttpServletRequest) request).getRequestURI());
            if (includeAddress.contains(ipAddress)) {
                chain.doFilter(request, response);
                return;
            }
            //uri
            String path = servletRequest.getRequestURI();
            logger.warn(String.format("unsupported ip address [%s] attempt to visit [%s]!", ipAddress, path));
        } else {
            logger.warn("unexpected warning information. unsupported request & response type!");
        }
        response.getWriter().append("you are not allowed to visit!");
    }

    @Override
    public void destroy() {

    }

}
